(*****************************************************************************
 * Delphi Plug-in Framework (DPF)
 * Copyright (C) 2006-2007 Tao Yuan
 *
 * The contents of this file are subject to the Mozilla Public License
 * Version 1.1 (the "License"); you may not use this file except in compliance
 * with the License. You may obtain a copy of the
 * License at http://www.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for
 * the specific language governing rights and limitations under the License.                                                               }
 *
 * The Initial Developer of the Original Code is Tao Yuan. Portions created by
 * Tao Yuan are Copyright (C) Tao Yuan (taoy.cn att gmail dott com)
 * All rights reserved.
 *****************************************************************************)
 
unit RDIntegrityChecker;

interface

uses
  Rcl,
  RDIdentity, RDIntegrityCheckReport, RDPluginRegistry, RDPathResolver,
  RDConsts, RDExceptions, RdClasses, LogLab;


type
  TIntegrityChecker = class;

  TReportItemImpl = class(TRdLogPersistent, IReportItem)
  private
    code: Integer;
    data: Rcl.IStrList;
    msg: string;
    severity: Byte;
    source: IIdentity;
  protected
    (**
     * @return error code for this report item
     *)
    function getCode: Integer;
    (**
     * @return message, associated with this report item
     *)
    function getMessage: String;
    (**
     * @return severity code for this report item
     *)
    function getSeverity: Byte;
    (**
     * @return source for this report item, can be <code>null</code>
     *)
    function getSource: IIdentity;
  public
    constructor Create(aSeverity: Byte; aSource: IIdentity; aCode: Integer; aMsg:
        string; aData: IStrList); overload;
    constructor Create(aSeverity: Byte; aSource: IIdentity; aCode: Integer; aMsg:
        string; aData: array of string); overload;

  end;

  TIntegrityChecker = class(TRdLogPersistent, IIntegrityCheckReport)
  private
    errorsCount: Integer;
    items: IIntfList;
    log: ILog;
    registry: IPluginRegistry;
    warningsCount: Integer;
    procedure checkPlugin(descr: IPluginDescriptor; pathResolver: IPathResolver);
  public
    constructor Create(aRegistry: IPluginRegistry; anItems: IIntfCollection);
        overload;
    constructor Create(aRegistry: IPluginRegistry; anItems: ICollection); overload;
    (**
     * @return number of items with severity {@link ReportItem#SEVERITY_ERROR}
     *         in this report
     *)
    function countErrors: Integer;
    (**
     * @return number of items with severity {@link ReportItem#SEVERITY_WARNING}
     *         in this report
     *)
    function countWarnings: Integer;
    procedure doCheck(pathResolver: IPathResolver);
    (**
     * @return collection of {@link ReportItem} objects
     *)
    function getItems: IIntfCollection;
  end;

implementation

uses
  SysUtils;

constructor TReportItemImpl.Create(aSeverity: Byte; aSource: IIdentity; aCode:
    Integer; aMsg: string; aData: array of string);
var
  I: Integer;
begin
  severity := aSeverity;
  source := aSource;
  code := aCode;
  msg := aMsg;
  if Length(aData) > 0 then
  begin
    data := TStrArrayList.Create;
    for I := 0 to High(aData) do
     data.Add(aData[I]);
  end;
end;

constructor TReportItemImpl.Create(aSeverity: Byte; aSource: IIdentity; aCode:
    Integer; aMsg: string; aData: IStrList);
begin
  severity := aSeverity;
  source := aSource;
  code := aCode;
  msg := aMsg;
  if aData <> nil then
    data := aData;
end;

function TReportItemImpl.getCode: Integer;
begin
  Result := code;
end;

function TReportItemImpl.getMessage: String;
begin
  Result := '';
//  Result := ResourceManager.getMessage('dpf.plugin.registry.xml', //$NON-NLS-1$
//          msg, data);
end;

function TReportItemImpl.getSeverity: Byte;
begin
  Result := severity;
end;

function TReportItemImpl.getSource: IIdentity;
begin
  Result := source;
end;

constructor TIntegrityChecker.Create(aRegistry: IPluginRegistry; anItems:
    IIntfCollection);
var
  it: IIntfIterator;
  item: IReportItem;
begin
  items := TIntfLinkedList.Create;
  registry := aRegistry;
  it := anItems.First;
  while it.HasNext do
  begin
    item := it.Next as IReportItem;
    if (item.getSeverity = SEVERITY_ERROR) then
      Inc(errorsCount)
    else if (item.getSeverity = SEVERITY_WARNING) then
      Inc(warningsCount);
    items.Add(item);
  end;
end;

constructor TIntegrityChecker.Create(aRegistry: IPluginRegistry; anItems:
    ICollection);
var
  it: IIterator;
  item: IReportItem;
begin
  items := TIntfLinkedList.Create;
  registry := aRegistry;
  it := anItems.First;
  while it.HasNext do
  begin
    Supports(it.Next, IReportItem, item);
    if (item.getSeverity = SEVERITY_ERROR) then
      Inc(errorsCount)
    else if (item.getSeverity = SEVERITY_WARNING) then
      Inc(warningsCount);
    items.Add(item);
  end;
end;

procedure TIntegrityChecker.checkPlugin(descr: IPluginDescriptor; pathResolver:
    IPathResolver);
var
  count: Integer;
  it: IIntfIterator;
  pre: IPluginPrerequisite;
  lib: ILibrary;
  url: string;
  extPoint: IExtensionPoint;
  it2: IIntfIterator;
  item: IReportItem;
  ext: IExtension;
  extItems: IIntfCollection;
begin
  // checking prerequisites
  items.add(TReportItemImpl.Create(SEVERITY_INFO, descr,
          ERROR_NO_ERROR, 'prerequisitesCheckStart', //$NON-NLS-1$
          [descr.getUniqueId]));
  it := descr.getPrerequisites.First;
  while it.HasNext do
  begin
    Supports(it.Next, IPluginPrerequisite, pre);
    Inc(count);
    if (not pre.isOptional) and (not pre.matches) then
    begin
      Inc(errorsCount);
      items.Add(TReportItemImpl.Create(SEVERITY_ERROR, descr,
                  ERROR_UNSATISFIED_PREREQUISITE,
                  'unsatisfiedPrerequisite', [
                  pre.getPluginId, descr.getUniqueId]));
    end;
  end;
  items.Add(TReportItemImpl.Create(SEVERITY_INFO, descr,
              ERROR_NO_ERROR, 'prerequisitesCheckFinish', [
              IntToStr(count), descr.getUniqueId]));
  // checking libraries
  if (pathResolver <> nil) then
  begin
    count := 0;
    items.add(TReportItemImpl.Create(SEVERITY_INFO, descr,
            ERROR_NO_ERROR, 'librariesCheckStart', //$NON-NLS-1$
            [descr.getUniqueId]));
    it := descr.getLibraries.First;
    while it.HasNext do
    begin
      Supports(it.Next, ILibrary, lib);
//      lib := ILibrary(it.Next);
      Inc(count);
      url := pathResolver.resolvePath(lib, lib.getPath);
      if not Rcl.FileUtils.IsFile(url) then
      begin
        Inc(errorsCount);
        items.Add(TReportItemImpl.Create(SEVERITY_ERROR, lib,
                      ERROR_BAD_LIBRARY,
                      'accesToResourceFailed', [ //$NON-NLS-1$
                      lib.getUniqueId, descr.getUniqueId, url]));
      end;
    end;
    items.add(TReportItemImpl.Create(SEVERITY_INFO, descr,
            ERROR_NO_ERROR, 'librariesCheckFinish', //$NON-NLS-1$
            [IntToStr(count), descr.getUniqueId]));

  end
  else
    items.add(TReportItemImpl.Create(SEVERITY_INFO, descr,
            ERROR_NO_ERROR, 'librariesCheckSkip', //$NON-NLS-1$
            [descr.getUniqueId]));

  // checking extension points
  count := 0;
  items.add(TReportItemImpl.Create(SEVERITY_INFO, descr,
          ERROR_NO_ERROR, 'extPointsCheckStart', nil)); //$NON-NLS-1$

  it := descr.getExtensionPoints.First;
  while True do
  begin
    Inc(count);
    Supports(it.Next, IExtensionPoint, extPoint);
//    extPoint := IExtensionPoint(it.Next);
    items.Add(TReportItemImpl.Create(SEVERITY_INFO, extPoint,
              ERROR_NO_ERROR, 'extPointCheckStart', //$NON-NLS-1$
              [extPoint.getUniqueId]));

    it2 := IExtensionPoint(extPoint).Validate.First;
    while it2.HasNext do
    begin
      Supports(it2.Next, IReportItem, item);
      if item.getSeverity = SEVERITY_ERROR then
        Inc(errorsCount)
      else if item.getSeverity = SEVERITY_WARNING then
        Inc(warningsCount);
      items.Add(TReportItemImpl.Create(SEVERITY_INFO, extPoint,
            ERROR_NO_ERROR, 'extPointCheckFinish', //$NON-NLS-1$
            [extPoint.getUniqueId]));
    end;
  end;

  items.add(TReportItemImpl.Create(SEVERITY_INFO, descr,
          ERROR_NO_ERROR, 'extPointsCheckFinish', //$NON-NLS-1$
          [IntToStr(count), descr.getUniqueId]));
  // checking extensions
  count := 0;
  items.add(TReportItemImpl.Create(SEVERITY_INFO, descr,
          ERROR_NO_ERROR, 'extsCheckStart', nil)); //$NON-NLS-1$
  it := descr.getExtensions.First;
  while it.HasNext do
  begin
    Inc(count);
    Supports(it.Next, IExtension, ext);
//    ext := IExtension(it.Next);
    items.Add(TReportItemImpl.Create(SEVERITY_INFO, ext,
              ERROR_NO_ERROR, 'extCheckStart', //$NON-NLS-1$
              [ext.getUniqueId]));
    extItems := ext.validate;
    it2 := extItems.First;
    while it2.HasNext do
    begin
      Supports(it2.Next, IReportItem, item);
      if (item.getSeverity = SEVERITY_ERROR) then
          Inc(errorsCount)
      else if (item.getSeverity = SEVERITY_WARNING) then
          Inc(warningsCount);
      items.add(item);
    end;
    items.add(TReportItemImpl.Create(SEVERITY_INFO, ext,
            ERROR_NO_ERROR, 'extCheckFinish', //$NON-NLS-1$
            [ext.getUniqueId]));
  end;
  items.add(TReportItemImpl.Create(SEVERITY_INFO, descr,
          ERROR_NO_ERROR, 'extsCheckFinish', //$NON-NLS-1$
          [IntToStr(count), descr.getUniqueId]));
end;

function TIntegrityChecker.countErrors: Integer;
begin
  Result := errorsCount;
end;

function TIntegrityChecker.countWarnings: Integer;
begin
  Result := warningsCount;
end;

procedure TIntegrityChecker.doCheck(pathResolver: IPathResolver);
var
  count: Integer;
  it: IIntfIterator;
  descr: IPluginDescriptor;
begin
  items.add(TReportItemImpl.Create(SEVERITY_INFO, nil,
          ERROR_NO_ERROR, 'pluginsCheckStart', nil)); //$NON-NLS-1$

  try
    it := registry.getPluginDescriptors.First;
    while it.HasNext do
    begin
      Supports(it.Next, IPluginDescriptor, descr);
      Inc(count);
      items.Add(TReportItemImpl.Create(
        SEVERITY_INFO, descr, SEVERITY_INFO, 'pluginCheckStart',
        [descr.getUniqueId]));
      checkPlugin(descr, pathResolver);
      items.Add(TReportItemImpl.Create(
        SEVERITY_INFO, descr, SEVERITY_INFO, 'pluginCheckFinish',
        [descr.getUniqueId]));

    end;
  except
    on e: Exception do
    begin
      log.error('integrity check failed for registry '{ + registry.}); //$NON-NLS-1$
      Inc(errorsCount);
      items.add(TReportItemImpl.Create(SEVERITY_ERROR, nil,
        ERROR_CHECKER_FAULT, 'pluginsCheckError', [e.Message])); //$NON-NLS-1$

    end;
  end;
  items.add(TReportItemImpl.Create(SEVERITY_INFO, nil,
          ERROR_NO_ERROR, 'pluginsCheckFinish', //$NON-NLS-1$
          [IntToStr(count)]));
end;

function TIntegrityChecker.getItems: IIntfCollection;
begin
  Result := items;
end;



end.
